package main

import (
	"global/libs/ws"
	"global/constants"
	"encoding/json"
	"github.com/ngaut/log"
	"time"
	"strings"
	"global/core"
	"global/util"
	"fmt"
	"github.com/shopspring/decimal"
	"sync"
)

type CoinDetail struct {
	Handler  		*Handler
	exit 			chan interface{}

	coin  			string
	exchangeName 	string
	rate 			string
	interval 		string
	length 			int

	summaryTicker 	*time.Ticker
	klineTicker 	*time.Ticker
	exchangeTicker 	*time.Ticker

	mux 			sync.Mutex
}

func (h *CoinDetail) ReadWriteMessage(handler *Handler, message ws.RequestMessage) error {
	h.Handler = handler
	err := h.dispatch(message)
	if err != nil {
		return err
	}
	return nil
}

func (h *CoinDetail) dispatch(message ws.RequestMessage) error {
	bytesMsg := []byte(message.Message)
	var err error
	switch message.MessageId {
	case constants.RequestCoinSummary:
		var req ws.CoinDetailSummaryRequest
		if err = json.Unmarshal(bytesMsg, &req); err == nil {
			h.handleSummary(&req)
		}
	case constants.RequestCoinKline:
		var req  ws.CoinDetailKlineRequest
		if err = json.Unmarshal(bytesMsg, &req); err == nil {
			h.handleKline(&req)
		}
	case constants.RequestCoinExchange:
		var req ws.CoinDetailExchangeRequest
		if err = json.Unmarshal(bytesMsg, &req); err == nil {
			h.handleExchange(&req)
		}
	}
	return err
}

func (h *CoinDetail) handleSummary(request *ws.CoinDetailSummaryRequest)  {
	h.coin = strings.ToLower(request.Coin)
	go func() {
		h.summary()
		for t := range h.summaryTicker.C {
			_ = t
			h.summary()
		}
	}()
}

func (h *CoinDetail) summary() {
	conn := h.Handler.ctx.GetBaseRedis().Get()
	defer conn.Close()
	ticker, err := core.GetCoinTicker(conn, h.coin)
	if err != nil {
		log.Errorf("Error: get ticker failed: %s", err)
		return
	}

	rate, err := core.GetCnyRateByCoin(conn, "usd")
	if err != nil {
		log.Errorf("Error: get rate failed: %s", err)
		return
	}

	resp := &ws.CoinDetailSummaryResponse{
		Symbol: 		strings.ToUpper(h.coin),
		Percent: 		ticker.Percent,
		Volume24h: 		ticker.Volume,
		PriceUsd: 		ticker.Close,
		PriceCny: 		util.GetUsdToCny(ticker.Close, rate),
		Amount24hCny: 	util.GetUsdToCny(ticker.Amount, rate),
		Amount24hUsd: 	ticker.Amount,
	}

	h.rate = rate

	h.Handler.response <- ws.WsResponse(resp, constants.RequestCoinSummary)
}

func (h *CoinDetail) handleKline(request *ws.CoinDetailKlineRequest) {
	if request.Coin == "" || request.Interval == "" {
		h.Handler.response <- ws.ErrorMsg(ws.ErrParameter, constants.RequestCoinKline)
		return
	}

	if _, ok := util.IntervalMap[request.Interval]; !ok {
		h.Handler.response <- ws.ErrorMsg(ws.ErrParameter, constants.RequestCoinKline)
		return
	}

	h.mux.Lock()
	h.interval = request.Interval
	h.coin = strings.ToLower(request.Coin)
	h.length = request.Length
	if request.Length < 200 || request.Length > 1000 {
		h.length = 200
	}
	h.mux.Unlock()
	log.Infof("coin: %s, interval: %s, length: %d", h.coin, h.interval, h.length)
	h.kline()
}

func (h *CoinDetail) kline() {
	if h.interval == "" || h.length < 200 { return }

	conn := h.Handler.ctx.GetBaseRedis().Get()
	defer conn.Close()

	// 获取length条数据
	res, err := core.GetAnalysisKlineCoinLists(conn, h.coin, h.interval, h.length)
	if err != nil {
		log.Errorf("Error: %s", err)
		h.Handler.response <- ws.ErrorMsg(ws.ErrInnerError, constants.RequestCoinKline)
		return
	}
	h.Handler.response<-ws.WsResponse(res, constants.RequestCoinKline)
}

func (h *CoinDetail) handleExchange(request *ws.CoinDetailExchangeRequest) {
	// check slug
	if request.Slug == "" {
		h.Handler.response <- ws.ErrorMsg(ws.ErrParameter, constants.RequestCoinExchange)
		return
	}
	h.exchangeName = strings.ToLower(request.Slug)
	h.coin = strings.ToLower(request.Coin)
}

func (h *CoinDetail) exchange() {
	if h.exchangeName == "" { return }

	conn := h.Handler.ctx.GetBaseRedis().Get()
	defer conn.Close()

	// get exchange
	symbols, err := core.GetPairsInExchangeForCoin(conn, h.exchangeName, h.coin)
	if err != nil {
		log.Errorf("Error: %s", err)
		return
	}

	tickers, err := core.GetPairTickersFromExchangeMulti(conn, h.exchangeName, symbols)
	if err != nil {
		log.Errorf("Error: %s", err)
		return
	}

	cp := make([]*ws.ExchangeCoinPair, 0)
	for _, v := range tickers {
		from := strings.ToUpper(v.From)
		to := strings.ToUpper(v.To)
		priceUsd, _ := decimal.NewFromString(v.PriceUsd)
		priceCny, _ := decimal.NewFromString(v.PriceCny)
		volume, _ := decimal.NewFromString(v.Volume)

		percent := v.Percent
		if v.Percent == "" {
			percent = decimal.Zero.StringFixed(4)
		}

		cp = append(cp, &ws.ExchangeCoinPair{
			From: 			from,
			To: 			to,
			Symbol: 		fmt.Sprintf("%s/%s", from, to),
			Volume24h: 		v.Volume,
			Amount24h: 		v.Amount,
			Amount24hUsd:	priceUsd.Mul(volume).StringFixedBank(8),
			Amount24hCny: 	priceCny.Mul(volume).StringFixedBank(8),
			Close: 			v.Close,
			PriceUsd: 		v.PriceUsd,
			PriceCny: 		v.PriceCny,
			Percent: 		percent,
		})
	}

	resp := struct {
		Data 		[]*ws.ExchangeCoinPair		`json:"data"`
		Exchange 	string						`json:"exchange"`
	}{
		Data:	cp,
		Exchange: 	h.exchangeName,
	}

	h.Handler.response <- ws.WsResponse(resp, constants.RequestCoinExchange)
}

func (h *CoinDetail) Init()  {
	h.exit = make(chan interface{})
	h.summaryTicker = time.NewTicker(2 * time.Second)
	h.klineTicker = time.NewTicker(30 * time.Second)
	h.exchangeTicker = time.NewTicker(5 * time.Second)
	go func() {
		for {
			select {
			case <-h.exit:
				h.summaryTicker.Stop()
				h.klineTicker.Stop()
				h.exchangeTicker.Stop()
				return
			case <-h.exchangeTicker.C:
				h.exchange()
			case <-h.klineTicker.C:
				h.kline()
			}
		}
	}()
}

func (h *CoinDetail) Clear()  {
	h.exit <- true
}

